# @Time : 2021/5/15 15:41
# @Author : Fioman 
# @Phone : 13149920693
import time

from tools.config_params import ConfigParams
from vision_process.image_operation import *
from tools.hd_logger import HdLogger
from db_tools.db_handler import DbHandler

cp = ConfigParams()
logger = HdLogger()
db = DbHandler()


def image_show(name, image, showState=False):
    """
    显示图片,用来调试的时候显示图片,按照一定的比例进行缩放
    :param name:显示的图片窗口名称
    :param image:显示的图片
    :param showState:是否显示
    :return:
    """
    if showState:
        sizeScale = 4
        newWidth = int(image.shape[1] / sizeScale)
        newHeight = int(image.shape[0] / sizeScale)
        showImage = cv.resize(image, (newWidth, newHeight), cv.INTER_AREA)
        cv.namedWindow("{}".format(name))
        cv.imshow("{}".format(name), showImage)
        cv.waitKey(0)


def find_total_board(image, width, height):
    # 1.先进行阈值分割
    thresUsed = np.mean(image[:150, :1100]) + 13
    filterAreaRate = 0.05
    T, board = cv.threshold(image, thresUsed, 255, cv.THRESH_BINARY)
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)

    # 2.然后将找到的所有的满足条件的区域,通过白线连接起来,形成整个物料框
    singleBoardArea = width * height
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > singleBoardArea * filterAreaRate]
    if len(largeContours) >= 2:
        centerPoints = [get_center_by_contour(contour) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        image_show("JointCenterLine", board)

        # 3.连线之后再找一遍
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntsFirstFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntsFirstFinded = largeContours[0]

    # 4.这个轮廓找到以后,将这个轮廓的四个角的影响去除掉
    rectPoints = np.int0(cv.boxPoints(cv.minAreaRect(cntsFirstFinded)))
    leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectPoints)

    # 5.去掉物料区域的四个角,再找一次,这里的宽高,应该用整个物料的尺寸去做
    totalWidth = distance(leftTop, rightTop)
    totalHeight = distance(leftTop, leftBottom)
    boardFinal = image.copy()
    moveWidth = int(totalWidth * 0.2)
    moveHeight = int(totalHeight * 0.2)
    extend = 20

    boardFinal[leftTop[1] - extend:leftTop[1] + moveHeight, leftTop[0] - extend:leftTop[0] + moveWidth] = 0
    boardFinal[rightTop[1] - extend:rightTop[1] + moveHeight, rightTop[0] - moveWidth:rightTop[0] + extend] = 0
    boardFinal[leftBottom[1] - moveHeight:leftBottom[1] + extend, leftBottom[0] - extend:leftBottom[0] + moveWidth] = 0
    boardFinal[rightBottom[1] - moveHeight:rightBottom[1] + extend, rightBottom[0] - moveWidth:rightBottom[0] + extend] = 0
    image_show("FinalBoardAngleMoved", boardFinal)

    T, board = cv.threshold(boardFinal, thresUsed, 255, cv.THRESH_BINARY)
    image_show("FinalBoardThres", board)

    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > singleBoardArea * filterAreaRate]

    if len(largeContours) >= 2:
        centerPoints = [get_center_by_contour(contour) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        image_show("JointCenterLine", board)

        # 6.连线之后再找一遍
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntFinalFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntFinalFinded = largeContours[0]

    totalBoardThres = boardFinal.copy()
    cv.drawContours(totalBoardThres, cntFinalFinded, -1, 255, -1)
    image_show("TotalBoardThres", totalBoardThres)

    box = cv.minAreaRect(cntFinalFinded)
    (cx, cy), (w, h), angle = box
    if angle > 70:
        angle = 90 - angle
        box = (cx, cy), (h, w), angle
    elif angle < -70:
        angle = 90 + angle
        box = (cx, cy), (h, w), angle

    imageColor = cv.cvtColor(image, cv.COLOR_GRAY2BGR)
    rectPoints = np.int0(cv.boxPoints(box))
    boxMask = np.zeros(image.shape, dtype=np.uint8)
    cv.drawContours(boxMask, [rectPoints], -1, 255, -1)
    cv.drawContours(imageColor, [rectPoints], -1, (0, 255, 0), 2)
    image_show("TotalBoard", imageColor)
    return box, totalBoardThres, imageColor


def put_table_sensor_pos_to_image(totalBoardRes, tableVal, sensorVal):
    """
    将选取的传感器位置和台布位置画到图上去.
    :param totalBoardRes: 返回的找到的物料区域的图像.
    :param tableVal: 台布的亮度均值
    :param sensorVal: 传感器的亮度均值
    :return:返回totalBoard加上传感器位置和台布位置信息的图像
    """
    tablePoints = [(cp.tablePosStart, cp.sensorPos - 100),
                   (cp.tablePosEnd, cp.sensorPos - 100),
                   (cp.tablePosEnd, cp.sensorPos),
                   (cp.tablePosStart, cp.sensorPos)]

    sensorPoints = [(cp.tablePosStart, cp.sensorPos),
                    (cp.tablePosEnd, cp.sensorPos),
                    (cp.tablePosEnd, cp.sensorPos + 100),
                    (cp.tablePosStart, cp.sensorPos + 100)]

    tableCenter = get_two_points_middle(tablePoints[0], tablePoints[1])
    sensorCenter = get_two_points_middle(sensorPoints[2], sensorPoints[3])

    # 画上框 和 下框,以及计算的值
    cv.drawContours(totalBoardRes, [np.array(tablePoints)], -1, (255, 0, 0), 3)
    cv.drawContours(totalBoardRes, [np.array(sensorPoints)], -1, (0, 0, 255), 3)
    cv.putText(totalBoardRes, str(tableVal), (int(tableCenter[0]), int(tableCenter[1] - 20)), cv.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0))
    cv.putText(totalBoardRes, str(sensorVal), (int(sensorCenter[0]), int(sensorCenter[1] + 50)), cv.FONT_HERSHEY_COMPLEX, 2, (0, 0, 255))
    image_show("TotalBoardTableVal", totalBoardRes)
    return totalBoardRes


def get_first_check_result(image, width, height, calcThresBig, totalBoardBox):
    """
     尺寸确认这里调用的代码逻辑,然后算子使用的是什么算子呢,算子保存到configParams里面.
    :param image:出入的图片
    :param width:要找的内板的宽
    :param height:内板的高
    :param calcThresBig:偏大阈值
    :param totalBoardBox:整个物料框的位置
    :return:
    """
    if image is None:
        return "error", "图像异常,请重拍", None, None, None
    totalBoard = get_board_widthout_angle(image, totalBoardBox)
    jointLineMask = np.zeros(image.shape, dtype=np.uint8)
    jointLineMask[:, cp.middleJointPos - 15:cp.middleJointPos + 15] = 255  # 中缝拼接处的处理
    jointLineMaskRotated = get_board_widthout_angle(jointLineMask, totalBoardBox)
    image_show("TotalBoard", totalBoard)

    _, (w, h), angle = totalBoardBox
    heightHalf = int(h / 2)
    # 先将图片分成两幅图
    upDownSlice = int(40 * cp.mm2pix)
    upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
    upBoardColor = cv.cvtColor(upBoard, cv.COLOR_GRAY2BGR)
    upJointLine = jointLineMaskRotated.copy()[:int(heightHalf + upDownSlice), :]

    upRes, upSizes, upBoxes = get_size_check_inner_board_info(upBoard, upJointLine, width, height, calcThresBig)
    upBoardResList = []
    upSizeResList = []
    if upRes == "ok":
        for index, upBox in enumerate(upBoxes):
            if upBox is not None:
                upBoardRes = upBoardColor.copy()
                cv.drawContours(upBoardRes, [np.array(upBox)], -1, (0, 255, 0), 2)
                cv.putText(upBoardRes, "{}".format(index + 1), (int(upBoardColor.shape[1] / 2 - 8), int(upBoardColor.shape[1] / 2)),
                           cv.FONT_HERSHEY_COMPLEX, 10, (255, 0, 0))
                image_show("upBoardRes", upBoardRes)
                upBoardResList.append(upBoardRes)
                upSizeResList.append(upSizes[index])
        return "ok", upSizeResList, upBoardResList, upBoardColor
    else:
        return "error", "内板识别出错", None, upBoardColor


def get_boxes(image, width, height, calcBig, calcSmall, innerEdgeThres, totalBoardBox, boardThresWithAngle, upLineOffset, downLineOffset):
    """
    正常的板获取内框图
    :param image: 原图
    :param width: 宽
    :param height: 高
    :param calcBig: 偏大阈值
    :param calcSmall: 偏小阈值
    :param innerEdgeThres: 位置正确阈值
    :param totalBoardBox: 整个物料框的位置信息
    :param boardThresWithAngle: board二值化有角度的
    :param upLineOffset: 上框的位置信息
    :param downLineOffset: 下框的位置信息
    :return:
    """
    _, (w, h), _ = totalBoardBox
    heightHalf = int(h / 2)
    totalBoard = get_board_widthout_angle(image, totalBoardBox)
    totalBoardColor = cv.cvtColor(totalBoard, cv.COLOR_GRAY2BGR)
    jointLineMask = np.zeros(image.shape, dtype=np.uint8)
    jointLineMask[:, cp.middleJointPos - 15:cp.middleJointPos + 15] = 255  # 中缝拼接处的处理
    jointLineMaskRotated = get_board_widthout_angle(jointLineMask, totalBoardBox)
    totalBoardThres = get_board_widthout_angle(boardThresWithAngle, totalBoardBox)
    image_show("TotalBoard", totalBoard)

    upDownSlice = int(40 * cp.mm2pix)
    upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
    downBoard = totalBoard.copy()[int(heightHalf - upDownSlice):, :]
    upJointLine = jointLineMaskRotated.copy()[:int(heightHalf + upDownSlice), :]
    downJointLine = jointLineMaskRotated.copy()[int(heightHalf - upDownSlice):, :]

    # 根据这两个框分别去去它的信息
    upRes, upInfo, upBox = get_inner_board_info(upBoard, upJointLine, width, height, calcBig, calcSmall, innerEdgeThres, "up", upLineOffset)
    downRes, downInfo, downBox = get_inner_board_info(downBoard, downJointLine, width, height, calcBig, calcSmall, innerEdgeThres,
                                                      "down", downLineOffset)
    if upRes == "ok" and downRes == "ok":
        downBoxPoints = [(p[0], p[1] + heightHalf - upDownSlice) for p in downBox]
        cv.drawContours(totalBoardColor, [np.array(upBox), np.array(downBoxPoints)], -1, (0, 255, 0), 2)
        image_show("TwoBoxes", totalBoardColor)
        downBox = downBoxPoints
        return True, "", upBox, downBox, totalBoard, totalBoardThres
    elif upRes == "sizeDif" or downRes == "sizeDif":
        return False, upInfo + downInfo, upBox, downBox, totalBoard, totalBoardThres
    else:
        return False, upInfo + downInfo, upBox, downBox, totalBoard, totalBoardThres


def get_boxes_first(image, width, height, calcBig, calcSmall, totalBoardBox, boardThresWithAngle):
    """
    首板的时候,进行识别,获取识别的内框,这个地方进行识别的时候,注意是没有板的尺寸位置信息的.
    :param image:
    :param width:
    :param height:
    :param calcBig:
    :param calcSmall:
    :param totalBoardBox:
    :param boardThresWithAngle:
    :return:
    """
    _, (w, h), _ = totalBoardBox
    heightHalf = int(h / 2)
    totalBoard = get_board_widthout_angle(image, totalBoardBox)
    totalBoardColor = cv.cvtColor(totalBoard, cv.COLOR_GRAY2BGR)
    jointLineMask = np.zeros(image.shape, dtype=np.uint8)
    jointLineMask[:, cp.middleJointPos - 15:cp.middleJointPos + 15] = 255  # 中缝拼接处的处理
    jointLineMaskRotated = get_board_widthout_angle(jointLineMask, totalBoardBox)
    totalBoardThres = get_board_widthout_angle(boardThresWithAngle, totalBoardBox)
    image_show("TotalBoard", totalBoard)

    upDownSlice = int(40 * cp.mm2pix)
    upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
    downBoard = totalBoard.copy()[int(heightHalf - upDownSlice):, :]
    upJointLine = jointLineMaskRotated.copy()[:int(heightHalf + upDownSlice), :]
    downJointLine = jointLineMaskRotated.copy()[int(heightHalf - upDownSlice):, :]

    # 根据这两个框分别去去它的信息
    upRes, upInfo, upBox = get_inner_board_info_first(upBoard, upJointLine, width, height, calcBig, calcSmall, "up")
    downRes, downInfo, downBox = get_inner_board_info_first(downBoard, downJointLine, width, height, calcBig, calcSmall, "down")
    if upRes == "ok" and downRes == "ok":
        downBoxPoints = [(p[0], p[1] + heightHalf - upDownSlice) for p in downBox]
        cv.drawContours(totalBoardColor, [np.array(upBox), np.array(downBoxPoints)], -1, (0, 255, 0), 2)
        image_show("TwoBoxes", totalBoardColor)
        downBox = downBoxPoints
        return True, "识别ok", upBox, downBox, totalBoard, totalBoardThres
    elif upRes == "sizeDif" or downRes == "sizeDif":
        return False, upInfo + downInfo, upBox, downBox, totalBoard, totalBoardThres
    else:
        return False, upInfo + downInfo, upBox, downBox, totalBoard, totalBoardThres


def get_inner_board_info(board, jointLine, width, height, calcBig, calcSmall, innerEdgeThres, boardPos, lineOffset):
    imageHeight, imageWidth = board.shape[:2]
    moveWidth = int(imageWidth * 0.2)
    moveHeight = int(imageHeight * 0.2)
    boardColor = cv.cvtColor(board.copy(), cv.COLOR_GRAY2BGR)
    sizeOkRes = []
    sizeDifRes = []
    calcArgs = cp.calcArgs
    res = {
        "info": "",
        "box": None,
    }
    # 算子打印出来
    print("使用的算子:")
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        print("{} : {}".format(str(tryTimes), calcArgs.get(str(tryTimes))))
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        cv.destroyAllWindows()
        gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate = calcArgs.get(str(tryTimes))
        print("进行第{}次计算,计算参数:(高斯半径:{}),(方差核大小:{}),(膨胀腐蚀核:{}),(膨胀腐蚀次数:{}),(腐蚀膨胀次数:{}),(阈值offset:{}),(scharr比例:{})".format(
            tryTimes, gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate))
        if len(gaussianR) == 1:
            if gaussianR[0] == 0:
                blurred = board.copy()
            else:
                blurred = cv.GaussianBlur(board, (0, 0), gaussianR[0])
        else:
            d, colorSigma = gaussianR
            spaceSigma = int(d / 2)
            blurred = cv.bilateralFilter(board, d, colorSigma, spaceSigma)

        if scharrRate == 0:
            scharrXY = blurred.copy()
        else:
            scharrX = cv.Scharr(blurred, cv.CV_64F, 1, 0)
            scharrY = cv.Scharr(blurred, cv.CV_64F, 0, 1)
            scharrX = cv.convertScaleAbs(scharrX)
            scharrY = cv.convertScaleAbs(scharrY)
            scharrXY = cv.addWeighted(scharrX, scharrRate, scharrY, scharrRate, 0)

        image_show("ScharrXY", scharrXY)
        img32 = np.float32(scharrXY)
        square1 = cv.blur(np.multiply(img32, img32), (squareR, squareR))
        square2 = np.multiply(cv.blur(img32, (squareR, squareR)), cv.blur(img32, (squareR, squareR)))
        squareDif32 = np.subtract(square1, square2)
        squareDif = squareDif32.copy()
        np.clip(squareDif32, 0, 255, out=squareDif)
        squareDif = squareDif.astype("uint8")
        image_show("SquareDif", squareDif)

        # 1.进行膨胀腐蚀,分割图像
        contourImg = squareDif.copy()
        kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, (dilateKernel, dilateKernel))
        contourImg = cv.dilate(contourImg, kernelUsed, iterations=dilateTimes)
        contourImg = cv.erode(contourImg, kernelUsed, iterations=dilateTimes)
        image_show("squareDifDilateErode", contourImg)

        squareDifErode = contourImg.copy()
        for index, thresOffset in enumerate(thresOffsetList):
            cv.destroyAllWindows()
            image_show("squareDifDilateErode", squareDifErode)
            # 2. 根据阈值进行分割
            thresUsed = squareDifErode[moveHeight:int(contourImg.shape[0] - moveHeight), moveWidth:contourImg.shape[1] - moveWidth]
            print("第{}个阈值,使用阈值 = {},thresOffset = {}".format(index + 1, np.mean(thresUsed), thresOffset))
            _, contourImg = cv.threshold(squareDifErode, np.mean(thresUsed) + thresOffset, 255, cv.THRESH_BINARY)

            # 3. 处理拼接处
            contourImg = cv.subtract(contourImg, jointLine)

            # 4. 进行泛洪填充
            mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
            cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
            floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
            floodFill[contourImg == 150] = 250
            image_show("FloodFill", floodFill)

            # 5.进行腐蚀膨胀,去除掉边界的噪点
            floodFill = cv.erode(floodFill, kernelUsed, iterations=erodeTimes)
            floodFill = cv.dilate(floodFill, kernelUsed, iterations=erodeTimes)
            image_show("FloodFillErodeDilate", floodFill)

            # 去除掉四个角的影响
            cv.rectangle(floodFill, (0, 0), (moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (0, floodFill.shape[0] - moveHeight), (moveWidth, floodFill.shape[1]), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1] - moveWidth, floodFill.shape[0] - moveHeight),
                         (floodFill.shape[1], floodFill.shape[0]), 0, -1)
            image_show("MoveAngle", floodFill)

            res = check_inner_board(floodFill, width, height, calcBig, calcSmall, innerEdgeThres, boardPos, lineOffset, boardColor)
            print("计算结果:{},尺寸:{},{},误差:{}".format(res["result"], res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                                   int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
            if res["result"] == "ok":
                sizeOkRes.append(res)
                if int(np.sum([abs(res["calcError"][0]), abs(res["calcError"][1])])) < 6:  # 如果不是做首片,并且阈值小于8个mm,就直接返回.
                    print("返回的ok结果是,尺寸:{},{},误差:{}".format(res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                             int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
                    return "ok", res["info"], res["box"]
            elif res["result"] == "big":
                sizeDifRes.append(res)
                if erodeTimes <= 15:
                    erodeTimes += 2
                continue
            elif res["result"] == "small":
                sizeDifRes.append(res)
                if erodeTimes > 8:
                    erodeTimes -= 2
                continue
    else:
        if len(sizeOkRes) >= 1:
            resOkSorted = sorted(sizeOkRes, key=lambda x: np.sum([abs(x["calcError"][0]), abs(x["calcError"][1])]))
            res = resOkSorted[0]
            print("返回的ok结果是,尺寸:{},{},误差:{}".format(res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                     int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
            return "ok", res["info"], res["box"]

        if len(sizeDifRes) >= 1:
            resSorted = sorted(sizeDifRes, key=lambda x: np.sum([abs(x["calcError"][0]), abs(x["calcError"][1])]))
            res = resSorted[0]
            return "sizeDif", res["info"], res["box"]
        else:
            return "error", res["info"], res["box"]


def get_inner_board_info_first(board, jointLine, width, height, calcBig, calcSmall, boardPos):
    """
    首片获取内板信息
    :param board:板子
    :param jointLine:拼接处
    :param width: 内板的宽
    :param height: 内板的高
    :param calcBig: 偏大阈值
    :param calcSmall: 偏小阈值
    :param boardPos: 板的位置,是上板还是下板
    :return:
    """
    imageHeight, imageWidth = board.shape[:2]
    moveWidth = int(imageWidth * 0.2)
    moveHeight = int(imageHeight * 0.2)
    boardColor = cv.cvtColor(board.copy(), cv.COLOR_GRAY2BGR)
    sizeOkRes = []
    sizeDifRes = []
    calcArgs = cp.calcArgs
    res = {
        "info": "",
        "box": None,
    }
    # 算子打印出来
    print("使用的算子:")
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        print("{} : {}".format(str(tryTimes), calcArgs.get(str(tryTimes))))
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        cv.destroyAllWindows()
        gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate = calcArgs.get(str(tryTimes))
        print("进行第{}次计算,计算参数:(高斯半径:{}),(方差核大小:{}),(膨胀腐蚀核:{}),(膨胀腐蚀次数:{}),(腐蚀膨胀次数:{}),(阈值offset:{}),(scharr比例:{})".format(
            tryTimes, gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate))
        if len(gaussianR) == 1:
            if gaussianR[0] == 0:
                blurred = board.copy()
            else:
                blurred = cv.GaussianBlur(board, (0, 0), gaussianR[0])
        else:
            d, colorSigma = gaussianR
            spaceSigma = int(d / 2)
            blurred = cv.bilateralFilter(board, d, colorSigma, spaceSigma)

        if scharrRate == 0:
            scharrXY = blurred.copy()
        else:
            scharrX = cv.Scharr(blurred, cv.CV_64F, 1, 0)
            scharrY = cv.Scharr(blurred, cv.CV_64F, 0, 1)
            scharrX = cv.convertScaleAbs(scharrX)
            scharrY = cv.convertScaleAbs(scharrY)
            scharrXY = cv.addWeighted(scharrX, scharrRate, scharrY, scharrRate, 0)

        image_show("ScharrXY", scharrXY)
        img32 = np.float32(scharrXY)
        square1 = cv.blur(np.multiply(img32, img32), (squareR, squareR))
        square2 = np.multiply(cv.blur(img32, (squareR, squareR)), cv.blur(img32, (squareR, squareR)))
        squareDif32 = np.subtract(square1, square2)
        squareDif = squareDif32.copy()
        np.clip(squareDif32, 0, 255, out=squareDif)
        squareDif = squareDif.astype("uint8")
        image_show("SquareDif", squareDif)

        # 1.进行膨胀腐蚀,分割图像
        contourImg = squareDif.copy()
        kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, (dilateKernel, dilateKernel))
        contourImg = cv.dilate(contourImg, kernelUsed, iterations=dilateTimes)
        contourImg = cv.erode(contourImg, kernelUsed, iterations=dilateTimes)
        image_show("squareDifDilateErode", contourImg)

        squareDifErode = contourImg.copy()
        for index, thresOffset in enumerate(thresOffsetList):
            cv.destroyAllWindows()
            image_show("squareDifDilateErode", squareDifErode)
            # 2. 根据阈值进行分割
            thresUsed = squareDifErode[moveHeight:int(contourImg.shape[0] - moveHeight), moveWidth:contourImg.shape[1] - moveWidth]
            print("第{}个阈值,使用阈值 = {},thresOffset = {}".format(index + 1, np.mean(thresUsed), thresOffset))
            _, contourImg = cv.threshold(squareDifErode, np.mean(thresUsed) + thresOffset, 255, cv.THRESH_BINARY)

            # 3. 处理拼接处
            contourImg = cv.subtract(contourImg, jointLine)

            # 4. 进行泛洪填充
            mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
            cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
            floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
            floodFill[contourImg == 150] = 250
            image_show("FloodFill", floodFill)

            # 5.进行腐蚀膨胀,去除掉边界的噪点
            floodFill = cv.erode(floodFill, kernelUsed, iterations=erodeTimes)
            floodFill = cv.dilate(floodFill, kernelUsed, iterations=erodeTimes)
            image_show("FloodFillErodeDilate", floodFill)

            # 去除掉四个角的影响
            cv.rectangle(floodFill, (0, 0), (moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (0, floodFill.shape[0] - moveHeight), (moveWidth, floodFill.shape[1]), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1] - moveWidth, floodFill.shape[0] - moveHeight),
                         (floodFill.shape[1], floodFill.shape[0]), 0, -1)
            image_show("MoveAngle", floodFill)

            res = check_inner_board_for_first_board(floodFill, width, height, calcBig, calcSmall, boardPos, boardColor)
            print("计算结果:{},尺寸:{},{},误差:{}".format(res["result"], res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                                   int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
            if res["result"] == "ok":
                sizeOkRes.append(res)
                if int(np.sum([abs(res["calcError"][0]), abs(res["calcError"][1])])) < 6:  # 如果不是做首片,并且阈值小于8个mm,就直接返回.
                    print("返回的ok结果是,尺寸:{},{},误差:{}".format(res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                             int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
                    return "ok", res["info"], res["box"]
            elif res["result"] == "big":
                sizeDifRes.append(res)
                if erodeTimes <= 15:
                    erodeTimes += 2
                continue
            elif res["result"] == "small":
                sizeDifRes.append(res)
                if erodeTimes > 8:
                    erodeTimes -= 2
                continue
    else:
        if len(sizeOkRes) >= 1:
            resOkSorted = sorted(sizeOkRes, key=lambda x: np.sum([abs(x["calcError"][0]), abs(x["calcError"][1])]))
            res = resOkSorted[0]
            print("返回的ok结果是,尺寸:{},{},误差:{}".format(res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                     int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
            return "ok", res["info"], res["box"]

        if len(sizeDifRes) >= 1:
            resSorted = sorted(sizeDifRes, key=lambda x: np.sum([abs(x["calcError"][0]), abs(x["calcError"][1])]))
            res = resSorted[0]
            return "sizeDif", res["info"], res["box"]
        else:
            return "error", res["info"], res["box"]


def get_size_check_inner_board_info(board, upJointLine, width, height, calcThresBig):
    """
    首板尺寸确认的时候调用的代码逻辑.
    :param board:
    :param upJointLine:
    :param width:
    :param height:
    :param calcThresBig:
    :return:
    """
    imageHeight, imageWidth = board.shape[:2]
    moveWidth = int(imageWidth * 0.2)
    moveHeight = int(imageHeight * 0.2)
    boardColor = cv.cvtColor(board.copy(), cv.COLOR_GRAY2BGR)
    sizeOkRes = []
    calcArgs = cp.calcArgs
    # 算子打印出来
    print("使用的算子:")
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        print("{} : {}".format(str(tryTimes), calcArgs.get(str(tryTimes))))
    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        cv.destroyAllWindows()
        gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate = calcArgs.get(str(tryTimes))
        print("进行第{}次计算,计算参数:(高斯半径:{}),(方差核大小:{}),(膨胀腐蚀核:{}),(膨胀腐蚀次数:{}),(腐蚀膨胀次数:{}),(阈值offset:{}),(scharr比例:{})".format(
            tryTimes, gaussianR, squareR, dilateKernel, dilateTimes, erodeTimes, thresOffsetList, scharrRate))
        if len(gaussianR) == 1:
            if gaussianR[0] == 0:
                blurred = board.copy()
            else:
                blurred = cv.GaussianBlur(board, (0, 0), gaussianR[0])
        else:
            d, colorSigma = gaussianR
            spaceSigma = int(d / 2)
            blurred = cv.bilateralFilter(board, d, colorSigma, spaceSigma)

        if scharrRate == 0:
            scharrXY = blurred.copy()
        else:
            scharrX = cv.Scharr(blurred, cv.CV_64F, 1, 0)
            scharrY = cv.Scharr(blurred, cv.CV_64F, 0, 1)
            scharrX = cv.convertScaleAbs(scharrX)
            scharrY = cv.convertScaleAbs(scharrY)
            scharrXY = cv.addWeighted(scharrX, scharrRate, scharrY, scharrRate, 0)

        image_show("ScharrXY", scharrXY)
        img32 = np.float32(scharrXY)
        square1 = cv.blur(np.multiply(img32, img32), (squareR, squareR))
        square2 = np.multiply(cv.blur(img32, (squareR, squareR)), cv.blur(img32, (squareR, squareR)))
        squareDif32 = np.subtract(square1, square2)
        squareDif = squareDif32.copy()
        np.clip(squareDif32, 0, 255, out=squareDif)
        squareDif = squareDif.astype("uint8")
        image_show("SquareDif", squareDif)

        # 1.进行膨胀腐蚀,分割图像
        contourImg = squareDif.copy()
        kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, (dilateKernel, dilateKernel))
        contourImg = cv.dilate(contourImg, kernelUsed, iterations=dilateTimes)
        contourImg = cv.erode(contourImg, kernelUsed, iterations=dilateTimes)
        image_show("squareDifDilateErode", contourImg)

        squareDifErode = contourImg.copy()
        for index, thresOffset in enumerate(thresOffsetList):
            cv.destroyAllWindows()
            image_show("squareDifDilateErode", squareDifErode)
            # 2. 根据阈值进行分割
            thresUsed = squareDifErode[moveHeight:int(contourImg.shape[0] - moveHeight), moveWidth:contourImg.shape[1] - moveWidth]
            print("第{}个阈值,使用阈值 = {},thresOffset = {}".format(index + 1, np.mean(thresUsed), thresOffset))
            _, contourImg = cv.threshold(squareDifErode, np.mean(thresUsed) + thresOffset, 255, cv.THRESH_BINARY)

            # 3. 处理拼接处
            contourImg = cv.subtract(contourImg, upJointLine)

            # 4. 进行泛洪填充
            mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
            cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
            floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
            floodFill[contourImg == 150] = 250
            image_show("FloodFill", floodFill)

            # 5.进行腐蚀膨胀,去除掉边界的噪点
            floodFill = cv.erode(floodFill, kernelUsed, iterations=erodeTimes)
            floodFill = cv.dilate(floodFill, kernelUsed, iterations=erodeTimes)
            image_show("FloodFillErodeDilate", floodFill)

            # 去除掉四个角的影响
            cv.rectangle(floodFill, (0, 0), (moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - moveWidth, moveHeight), 0, -1)
            cv.rectangle(floodFill, (0, floodFill.shape[0] - moveHeight), (moveWidth, floodFill.shape[1]), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1] - moveWidth, floodFill.shape[0] - moveHeight),
                         (floodFill.shape[1], floodFill.shape[0]), 0, -1)
            image_show("MoveAngle", floodFill)

            res = check_inner_board_for_size_check(floodFill, width, height, calcThresBig, boardColor)
            print("计算结果:{},尺寸:{},{},误差:{}".format(res["result"], res["calcSize"], (int(res["calcSize"][0] * cp.mm2pix),
                                                                                   int(res["calcSize"][1] * cp.mm2pix)), res["calcError"]))
            if res["result"] != "error":
                sizeOkRes.append(res)
                continue
            elif res["result"] == "big":
                if erodeTimes <= 15:
                    erodeTimes += 2
                continue
            elif res["result"] == "small":
                if erodeTimes > 8:
                    erodeTimes -= 2
                continue
    else:
        sizeList = []
        boxList = []
        if len(sizeOkRes) >= 1:
            resOkSorted = sorted(sizeOkRes, key=lambda x: np.sum([abs(x["calcError"][0]), abs(x["calcError"][1])]))
            for res in resOkSorted:
                sizeList.append(res["calcSize"])
                boxList.append(res["box"])
            return "ok", sizeList, boxList
        else:
            return "error", [], []


def check_inner_board(board, width, height, calcBig, calcSmall, innerEdge, boardPos, lineOffset, boardColor):
    """
    :param board:
    :param width:
    :param height:
    :param calcBig:
    :param calcSmall:
    :param innerEdge:
    :param boardPos:
    :param lineOffset:
    :param boardColor
    :return:
    """
    useImage = boardColor.copy()
    totalHeight, totalWidth = board.shape[:2]
    boardArea = width * height
    totalWidth = round(totalWidth / cp.mm2pix, 2)
    totalHeight = round(totalHeight / cp.mm2pix, 2)
    res = {
        "result": "error",
        "box": None,
        "info": "",
        "calcSize": (totalWidth, totalHeight),
        "calcError": (0, 0),
    }
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    try:
        _, (w, h), _ = cv.minAreaRect(cntsSorted[0])
        areaFinded = w * h
        if len(cnts) == 0:
            res["result"] = "error"
            res["info"] = "{} 找内板失败,没有找到任何轮廓".format(boardPos)
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
            return res
        elif 1.3 * boardArea > areaFinded > 0.8 * boardArea:
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            cv.drawContours(useImage, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImage)
            leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectBoxPoints)
            widthFinded = round(distance(leftTop, rightTop) / cp.mm2pix, 2)  # 找到的宽,单位mm
            heightFinded = round(distance(leftTop, leftBottom) / cp.mm2pix, 2)  # 找到的高,单位mm
            widthError, heightError = round(widthFinded - width / cp.mm2pix, 2), round(heightFinded - height / cp.mm2pix, 2)
            res["calcSize"] = (widthFinded, heightFinded)
            res["calcError"] = (widthError, heightError)
            # 过滤掉一定是错误的线.第一,如果找到的两个顶点
            # 如果找到的第一边的一个顶点,太接近物料的顶点,就认为这条线肯定是找错了.
            if min(leftTop[1], rightTop[1]) < 8 * cp.mm2pix and boardPos == "up":
                res["result"] = "error"
                res["info"] = "{}框 找到的内板第一边,太靠近上边缘,判定找板失败".format(boardPos)
                return res

            if max(leftBottom[1], rightBottom[1]) > (totalHeight - 10) * cp.mm2pix and boardPos == "up":
                res["result"] = "error"
                res["info"] = "{}框 找到的内板中缝,太靠近下边缘,判定找板失败".format(boardPos)
                return res

            widthMax = width / cp.mm2pix + calcBig
            widthMin = width / cp.mm2pix - calcSmall
            heightMax = height / cp.mm2pix + calcBig
            heightMin = height / cp.mm2pix - calcSmall
            res["box"] = [leftTop, rightTop, rightBottom, leftBottom]
            if widthFinded < widthMin or widthFinded > widthMax:
                otherLineIsOk, newBox = check_other_line_is_ok(board, res["box"], lineOffset, innerEdge, "13", width, boardPos)
                if otherLineIsOk:
                    res["box"] = newBox
                    leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(newBox)
                    widthFinded = distance(leftTop, rightTop)
                    heightFinded = distance(leftTop, leftBottom)
                    res["calcSize"] = (round(widthFinded / cp.mm2pix, 2), round(heightFinded / cp.mm2pix, 2))
                    res["calcError"] = (abs(round((widthFinded - width) / cp.mm2pix, 2)), abs(round((heightFinded - height) / cp.mm2pix)))
                    res["info"] = "识别成功,通过三边推测第三边"
                    useImg = boardColor.copy()
                    cv.drawContours(useImg, [np.array(newBox)], -1, (0, 255, 0), 3)
                    image_show("InnerBoardFindedByExtend", useImg)
                    res["result"] = "ok"
                    return res
                else:
                    if widthFinded < widthMin:
                        res["result"] = "small"
                        res["info"] = "宽度识别过小,识别尺寸:({},{})".format(res["calcSize"][0], res["calcSize"][1])
                        return res
                    else:
                        res["result"] = "big"
                        res["info"] = "宽度识别过大,识别尺寸:({},{})".format(res["calcSize"][0], res["calcSize"][1])
                        return res

            if heightFinded < heightMin or heightFinded > heightMax:
                otherLineIsOk, newBox = check_other_line_is_ok(board, res["box"], lineOffset, innerEdge, "24", height, boardPos)
                if otherLineIsOk:
                    res["box"] = newBox
                    leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(newBox)
                    widthFinded = distance(leftTop, rightTop)
                    heightFinded = distance(leftTop, leftBottom)
                    res["calcSize"] = (round(widthFinded / cp.mm2pix, 2), round(heightFinded / cp.mm2pix, 2))
                    res["calcError"] = (abs(round((widthFinded - width) / cp.mm2pix, 2)), abs(round((heightFinded - height) / cp.mm2pix)))
                    res["info"] = "识别成功,通过三边推测第三边"
                    useImg = boardColor.copy()
                    cv.drawContours(useImg, [np.array(newBox)], -1, (0, 255, 0), 3)
                    image_show("InnerBoardFindedByExtend", useImg)
                    res["result"] = "ok"
                    return res
                else:
                    if heightFinded < heightMin:
                        res["result"] = "small"
                        res["info"] = "高度识别过小,识别尺寸:({},{})".format(res["calcSize"][0], res["calcSize"][1])
                        return res
                    else:
                        res["result"] = "big"
                        res["info"] = "高度识别过大,识别尺寸:({},{})".format(res["calcSize"][0], res["calcSize"][1])
                        return res

            res["result"] = "ok"
            res["info"] = "{} 识别成功!".format(boardPos)
            return res
        else:
            res["result"] = "error"
            res["info"] = "识别的面积过大"
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix))
            return res
    except Exception as e:
        errorInfo = "in check_inner_board() error:{}".format(str(e))
        res["result"] = "error"
        res["info"] = errorInfo
        res["calcSize"] = (totalWidth, totalHeight)
        res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
        return res


def get_line_offset(upBox, downBox, totalBoard, totalBoardBox):
    """
    获取识别的内框的offset,单位是像素
    :param upBox: 上框位置
    :param downBox: 下框位置
    :param totalBoard: 整个物料区域
    :param totalBoardBox: 整个物料区域的位置信息
    :return:
    """
    if len(totalBoard.shape) >= 3:
        totalBoard = cv.cvtColor(totalBoard, cv.COLOR_BGR2GRAY)
    w, h = totalBoardBox[1]
    heightHalf = int(h / 2)
    upDownSlice = int(40 * cp.mm2pix)
    upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
    upH, upW = upBoard.shape[:2]
    p1 = (0, 0)
    p2 = (upW, 0)
    p3 = (upW, upH)
    p4 = (0, upH)
    p5, p6, p7, p8 = upBox
    line1, line2, line3, line4 = [(p1, p2), (p2, p3), (p3, p4), (p4, p1)]
    line5, line6, line7, line8 = [(p5, p6), (p6, p7), (p7, p8), (p8, p5)]
    offset1 = get_two_line_distance(line1, line5)
    offset2 = get_two_line_distance(line2, line6)
    offset3 = get_two_line_distance(line3, line7)
    offset4 = get_two_line_distance(line4, line8)
    upOffset = [int(offset1), int(offset2), int(offset3), int(offset4)]

    # 下框的额坐标也要是totalBoard上的坐标
    p1 = (0, heightHalf - upDownSlice)
    p2 = (w, heightHalf - upDownSlice)
    p3 = (w, h)
    p4 = (0, h)
    p5, p6, p7, p8 = downBox
    line1, line2, line3, line4 = [(p1, p2), (p2, p3), (p3, p4), (p4, p1)]
    line5, line6, line7, line8 = [(p5, p6), (p6, p7), (p7, p8), (p8, p5)]
    offset1 = get_two_line_distance(line1, line5)
    offset2 = get_two_line_distance(line2, line6)
    offset3 = get_two_line_distance(line3, line7)
    offset4 = get_two_line_distance(line4, line8)
    downOffset = [int(offset1), int(offset2), int(offset3), int(offset4)]
    return upOffset, downOffset


def check_other_line_is_ok(board, box, lineOffset, innerEdge, checkPos, extend, boardPos):
    """
    检测其他的线是否OK
    :param board:
    :param box
    :param lineOffset:
    :param innerEdge:
    :param checkPos:检测的位置,13,表示检测13刀的位置,宽度检测有问题,这是时候去验证高度的距离位置是否在监控的范围内.如果再就通过延长线的方式重新画线.
    :param extend:切线需要延长的距离.
    :param boardPos, 是上框还是下框,主要是用来判定长铜箔的问题,如果是长铜箔就认为不是OK的.上框的时候是第一边,下框的时候是第三边
    :return:
    """
    longCropperMmLimit = 80  # 长铜箔不推测的mm限制,防止长铜箔折叠把板只遮挡起来
    h, w = board.shape[:2]
    p1 = (0, 0)
    p2 = (w, 0)
    p3 = (w, h)
    p4 = (0, h)
    p5, p6, p7, p8 = box
    line1, line2, line3, line4 = [(p1, p2), (p2, p3), (p3, p4), (p4, p1)]
    line5, line6, line7, line8 = [(p5, p6), (p6, p7), (p7, p8), (p8, p5)]
    offset1 = get_two_line_distance(line1, line5)
    offset2 = get_two_line_distance(line2, line6)
    offset3 = get_two_line_distance(line3, line7)
    offset4 = get_two_line_distance(line4, line8)
    innerEdge = innerEdge * cp.mm2pix
    offsetCheckVal = np.subtract(np.array(lineOffset), np.array([offset1, offset2, offset3, offset4])).tolist()
    offsetCheckVal = [abs(x) for x in offsetCheckVal]
    isOkList = [True if offsetCheck <= innerEdge else False for offsetCheck in offsetCheckVal]
    leftTop, rightTop, rightBottom, leftBottom = box
    if checkPos == "13":
        if isOkList[0] and isOkList[1] and isOkList[2]:
            # 这里过滤掉是长铜箔的情况
            if lineOffset[3] > longCropperMmLimit * cp.mm2pix:
                return False, box
            # 如果1,2,3的切线位置是对的,就推测第四条切线.顺时针方向的四条线1,2,3,4
            topK, topB = get_line_k_and_b(leftTop, rightTop)
            bottomK, bottomB = get_line_k_and_b(leftBottom, rightBottom)
            if topK == 0 or bottomK == 0:
                newLeftTop = [int(rightTop[0] - extend), rightTop[1]]
                newLeftBottom = [int(rightBottom[0] - extend), rightBottom[1]]
                newRightTop = rightTop
                newRightBottom = rightBottom
            else:
                xOffset = abs(extend * np.cos(trans_k_to_rad(topK)))  # 先将延长的距离,换算成x坐标的跨度.
                leftTopX = int(rightTop[0] - xOffset)
                leftTopY = int(topK * leftTopX + topB)
                leftBottomX = int(rightBottom[0] - xOffset)
                leftBottomY = int(bottomK * leftBottomX + bottomB)
                newLeftTop = [leftTopX, leftTopY]
                newLeftBottom = [leftBottomX, leftBottomY]
                newRightTop = rightTop
                newRightBottom = rightBottom
            return True, [newLeftTop, newRightTop, newRightBottom, newLeftBottom]
        elif isOkList[0] and isOkList[2] and isOkList[3]:
            if lineOffset[1] > longCropperMmLimit * cp.mm2pix:
                return False, box
            # 如果1,3,4的切线是对的,就推测第2条线.顺时针方向的四条线1,2,3,4
            topK, topB = get_line_k_and_b(leftTop, rightTop)
            bottomK, bottomB = get_line_k_and_b(leftBottom, rightBottom)
            if topK == 0 or bottomK == 0:
                newRightTop = [int(leftTop[0] + extend), rightTop[1]]
                newRightBottom = [int(leftBottom[0] + extend), rightBottom[1]]
                newLeftTop = leftTop
                newLeftBottom = leftBottom
            else:
                xOffset = abs(extend * np.cos(trans_k_to_rad(topK)))  # 先将延长的距离,换算成x坐标的跨度.
                rightTopX = int(leftTop[0] + xOffset)
                rightTopY = int(topK * rightTopX + topB)
                rightBottomX = int(leftBottom[0] + xOffset)
                rightBottomY = int(bottomK * rightBottomX + bottomB)
                newRightTop = [rightTopX, rightTopY]
                newRightBottom = [rightBottomX, rightBottomY]
                newLeftTop = leftTop
                newLeftBottom = leftBottom
            return True, [newLeftTop, newRightTop, newRightBottom, newLeftBottom]
        else:
            return False, box

    else:
        # 如果是调整高度的话.
        if isOkList[1] and isOkList[3] and isOkList[0]:  # 推测第三边
            if lineOffset[2] > longCropperMmLimit * cp.mm2pix and boardPos == "down":
                return False, box
            leftK, leftB = get_line_k_and_b(leftTop, leftBottom)
            rightK, rightB = get_line_k_and_b(rightTop, rightBottom)
            if leftK is None or rightK is None:
                newLeftBottom = [leftTop[0], leftTop[1] + extend]
                newRightBottom = [rightTop[0], rightTop[1] + extend]
                newLeftTop = leftTop
                newRightTop = rightTop
            else:
                yOffset = abs(extend * np.sin(trans_k_to_rad(leftK)))
                leftBottomY = int(leftTop[1] + yOffset)
                leftBottomX = int((leftBottomY - leftB) / leftK)
                rightBottomY = int(rightTop[1] + yOffset)
                rightBottomX = int((rightBottomY - rightB) / rightK)
                newLeftBottom = [leftBottomX, leftBottomY]
                newRightBottom = [rightBottomX, rightBottomY]
                newLeftTop = leftTop
                newRightTop = rightTop
            return True, [newLeftTop, newRightTop, newRightBottom, newLeftBottom]
        elif isOkList[1] and isOkList[3] and isOkList[2]:  # 推测第一边
            if lineOffset[0] > longCropperMmLimit * cp.mm2pix and boardPos == "up":
                return False, box
            leftK, leftB = get_line_k_and_b(leftTop, leftBottom)
            rightK, rightB = get_line_k_and_b(rightTop, rightBottom)
            if leftK is None or rightK is None:
                newLeftTop = [leftTop[0], leftBottom[1] - extend]
                newRightTop = [rightTop[0], rightBottom[1] - extend]
                newLeftBottom = leftBottom
                newRightBottom = rightBottom
            else:
                yOffset = abs(extend.np.sin(trans_k_to_rad(leftK)))
                leftTopY = int(leftBottom[1] - yOffset)
                leftTopX = int((leftTopY - leftB) / leftK)
                rightTopY = int(rightBottom[1] - yOffset)
                rightTopX = int((rightTopY - rightB) / rightK)
                newLeftTop = [leftTopX, leftTopY]
                newRightTop = [rightTopX, rightTopY]
                newLeftBottom = leftBottom
                newRightBottom = rightBottom
            return True, [newLeftTop, newRightTop, newRightBottom, newLeftBottom]
        else:
            return False, box


def check_inner_board_for_first_board(board, width, height, calcBig, calcSmall, boardPos, boardColor):
    """
    做首板的时候,对识别的内板的尺寸进行检测.
    :param board:
    :param width:
    :param height:
    :param calcBig:
    :param calcSmall:
    :param boardPos:
    :param boardColor:彩色图
    :return:
    """
    # 尺寸确认的时候,只使用偏大阈值
    useImage = boardColor.copy()
    totalHeight, totalWidth = board.shape[:2]
    boardArea = width * height
    totalWidth = round(totalWidth / cp.mm2pix, 2)
    totalHeight = round(totalHeight / cp.mm2pix, 2)
    if boardPos == "up":
        boardPos = "上框"
    else:
        boardPos = "下框"
    res = {
        "result": "error",
        "box": None,
        "info": "",
        "calcSize": (totalWidth, totalHeight),
        "calcError": (0, 0),
    }
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    try:
        _, (w, h), _ = cv.minAreaRect(cntsSorted[0])
        areaFinded = w * h
        if len(cnts) == 0:
            res["result"] = "error"
            res["info"] = "{} 找内板失败,没有找到任何轮廓".format(boardPos)
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
            return res
        elif 1.3 * boardArea > areaFinded > 0.8 * boardArea:
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            cv.drawContours(useImage, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImage)
            leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectBoxPoints)
            widthFinded = round(distance(leftTop, rightTop) / cp.mm2pix, 2)  # 找到的宽,单位mm
            heightFinded = round(distance(leftTop, leftBottom) / cp.mm2pix, 2)  # 找到的高,单位mm
            widthError, heightError = round(widthFinded - width / cp.mm2pix, 2), round(heightFinded - height / cp.mm2pix, 2)
            res["calcSize"] = (widthFinded, heightFinded)
            res["calcError"] = (widthError, heightError)
            # 过滤掉一定是错误的线.第一,如果找到的两个顶点
            # 如果找到的第一边的一个顶点,太接近物料的顶点,就认为这条线肯定是找错了.
            if min(leftTop[1], rightTop[1]) < 8 * cp.mm2pix:
                res["result"] = "error"
                res["info"] = "{}框 找到的内板第一边,太靠近上边缘,判定找板失败".format(boardPos)
                return res

            if max(leftBottom[1], rightBottom[1]) > (totalHeight - 10) * cp.mm2pix:
                res["result"] = "error"
                res["info"] = "{}框 找到的内板中缝,太靠近下边缘,判定找板失败".format(boardPos)
                return res

            widthMax = width / cp.mm2pix + calcBig
            widthMin = width / cp.mm2pix - calcSmall
            heightMax = height / cp.mm2pix + calcBig
            heightMin = height / cp.mm2pix - calcSmall
            res["box"] = [leftTop, rightTop, rightBottom, leftBottom]
            if widthFinded < widthMin or heightFinded < heightMin:
                res["result"] = "small"
                res["info"] = "{}框 识别尺寸过小,识别尺寸:{}".format(boardPos, res["calcSize"])
                return res

            if widthFinded > widthMax or heightFinded > heightMax:
                res["result"] = "big"
                res["info"] = "{}框 识别尺寸过大,识别尺寸:{}".format(boardPos, res["calcSize"])
                return res
            res["result"] = "ok"
            res["info"] = "{} 识别成功!".format(boardPos)
            return res
        else:
            res["result"] = "error"
            res["info"] = "识别的面积过大"
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix))
            return res
    except Exception as e:
        errorInfo = "in check_inner_board_for_first_board() error:{}".format(str(e))
        res["result"] = "error"
        res["info"] = errorInfo
        res["calcSize"] = (totalWidth, totalHeight)
        res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
        return res


def check_inner_board_for_size_check(boardMoveAngle, width, height, calcThresBig, boardColor):
    """
    尺寸确认那里的识别检测,这里会把识别的尺寸无论是偏大还是偏小,只要找到的是对的就返回给.
    :param boardMoveAngle:最后处理掉的包含内板的二值图像
    :param width:内板的宽
    :param height:内板的高
    :param calcThresBig:偏大阈值,首次尺寸确认的时候只使用偏大阈值
    :param boardColor:
    :return:
    """
    # 尺寸确认的时候,只使用偏大阈值
    useImage = boardColor.copy()
    totalHeight, totalWidth = boardMoveAngle.shape[:2]
    boardArea = width * height
    totalWidth = round(totalWidth / cp.mm2pix, 2)
    totalHeight = round(totalHeight / cp.mm2pix, 2)
    res = {
        "result": "error",
        "box": None,
        "info": "",
        "calcSize": (totalWidth, totalHeight),
        "calcError": (0, 0),
    }
    cnts = cv.findContours(boardMoveAngle.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    try:
        _, (w, h), _ = cv.minAreaRect(cntsSorted[0])
        areaFinded = w * h
        if len(cnts) == 0:
            res["result"] = "error"
            res["info"] = "找内板失败,没有找到任何轮廓"
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
            return res
        elif 1.3 * boardArea > areaFinded > 0.8 * boardArea:
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            cv.drawContours(useImage, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImage)
            leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectBoxPoints)
            widthFinded = round(distance(leftTop, rightTop) / cp.mm2pix, 2)  # 找到的宽,单位mm
            heightFinded = round(distance(leftTop, leftBottom) / cp.mm2pix, 2)  # 找到的高,单位mm
            widthError, heightError = round(widthFinded - width / cp.mm2pix, 2), round(heightFinded - height / cp.mm2pix, 2)
            res["calcSize"] = (widthFinded, heightFinded)
            res["calcError"] = (widthError, heightError)
            # 过滤掉一定是错误的线.第一,如果找到的两个顶点
            # 如果找到的第一边的一个顶点,太接近物料的顶点,就认为这条线肯定是找错了.
            if min(leftTop[1], rightTop[1]) < 8 * cp.mm2pix:
                res["result"] = "error"
                res["info"] = "找到的内板第一边,太靠近上边缘,判定找板失败"
                return res

            if max(leftBottom[1], rightBottom[1]) > (totalHeight - 10) * cp.mm2pix:
                res["result"] = "error"
                res["info"] = "找到的内板中缝,太靠近下边缘,判定找板失败"
                return res

            widthMax = width / cp.mm2pix + calcThresBig
            widthMin = width / cp.mm2pix - calcThresBig
            heightMax = height / cp.mm2pix + calcThresBig
            heightMin = height / cp.mm2pix - calcThresBig
            res["box"] = [leftTop, rightTop, rightBottom, leftBottom]
            if widthFinded < widthMin or heightFinded < heightMin:
                res["result"] = "small"
                res["info"] = "识别尺寸过小"
                return res

            if widthFinded > widthMax or heightFinded > heightMax:
                res["result"] = "big"
                res["info"] = "识别尺寸过大"
                return res
            res["result"] = "ok"
            res["info"] = "识别成功!"
            return res
        else:
            res["result"] = "error"
            res["info"] = "识别的面积过大"
            res["calcSize"] = (totalWidth, totalHeight)
            res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix))
            return res
    except Exception as e:
        errorInfo = "in check_inner_board_first_check() error:{}".format(str(e))
        res["result"] = "error"
        res["info"] = errorInfo
        res["calcSize"] = (totalWidth, totalHeight)
        res["calcError"] = (round(totalWidth - width / cp.mm2pix, 2), round(totalHeight - height / cp.mm2pix, 2))
        return res


def get_board_edge_lines(totalBoard, totalBoardThres):
    try:
        totalBoardColor = cv.cvtColor(totalBoard, cv.COLOR_GRAY2BGR)
        board = totalBoardThres.copy()[:int(totalBoardThres.shape[0] / 4), :]
        scharrX = cv.Scharr(board, cv.CV_64F, 1, 0)
        scharrY = cv.Scharr(board, cv.CV_64F, 0, 1)
        scharrX = cv.convertScaleAbs(scharrX)
        scharrY = cv.convertScaleAbs(scharrY)
        scharrXY = cv.addWeighted(scharrX, 0.5, scharrY, 0.5, 0)
        board = scharrXY.copy()
        image_show("ScharrXYEdge ", board)
        minLineLength = 150
        maxLineGap = 5
        lines = cv.HoughLinesP(board, 1, np.pi / 180, 100, minLineLength, maxLineGap)
        board1 = totalBoardColor.copy()
        linesList = []
        for line in lines:
            for x1, y1, x2, y2 in line:
                lineLength = distance((x1, y1), (x2, y2))
                if abs(y2 - y1) < 20 * cp.mm2pix and (x2 - x1) > 10 * cp.mm2pix and y1 < 20 and y2 < 20 and lineLength > 80 * cp.mm2pix:
                    linesList.append([x1, y1, (y1 + y2) / 2])
                    linesList.append([x2, y2, (y1 + y2) / 2])
                    cv.line(board1, (x1, y1), (x2, y2), (0, 255, 0), 3)
        image_show("EdgeFinded", board1)

        print("linesFinalFinded:{}个,{}".format(len(linesList), linesList))
        lineRate = 2 / 3
        if len(linesList) >= 10:
            lineLength = len(linesList)
            end = int(lineLength * lineRate)
            if end % 2 != 0:
                end -= 1
            linesListFinal = sorted(linesList, key=lambda x: x[2])[:end]
            print("linesFinalFinded     :{}".format(linesListFinal))
            linesListFinal = [line[:2] for line in linesListFinal]
            topCutLineK, topCutLineB = get_linear_fit_k_and_b_horizontal(np.array(linesListFinal))
            topCutAngle = np.arctan(topCutLineK) * 180 / np.pi
            print(topCutAngle)
            if abs(topCutAngle) > 1.2:
                return None, None
            return topCutLineK, topCutLineB
        elif len(linesList) >= 4:
            lineLength = len(linesList)
            end = int(lineLength * lineRate)
            if end % 2 != 0:
                end -= 1
            linesListFinal = sorted(linesList, key=lambda x: x[2])[:end]
            print("linesFinalFinded     :{}".format(linesListFinal))
            linesListFinal = [line[:2] for line in linesListFinal]
            topCutLineK, topCutLineB = get_linear_fit_k_and_b_horizontal(np.array(linesListFinal))
            topCutAngle = np.arctan(topCutLineK) * 180 / np.pi
            print(topCutAngle)
            if abs(topCutAngle) > 1.2:
                return None, None
            return topCutLineK, topCutLineB
        else:
            return None, None
    except Exception as e:
        print(str(e))
        return None, None


def get_lines_with_boxes(image, totalBoard, totalBoardBox, totalBoardThres, upBox, downBox, transMtx, topRemain, leftRemain,
                         bottomRemain, rightRemain):
    if len(image.shape) == 3:
        image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    imageColor = cv.cvtColor(image, cv.COLOR_GRAY2BGR)

    _, (w, h), totalBoardAngle = totalBoardBox
    # print ((cx, cy), (w, h), totalBoardAngle)
    upLeftTop, upRightTop, upRightBottom, upLeftBottom = upBox
    downLeftTop, downRightTop, downRightBottom, downLeftBottom = downBox
    # cut_remain_pix_acbd如果不等于-1，则证明需要切割ac，bd边，那就需要进行下面的计算

    leftCutLineK = get_linear_fit_k_vertical(np.array([upLeftTop, upLeftBottom, downLeftTop, downLeftBottom]))
    # 直接分别求过四个点的直线的b参数，找出最值
    if leftCutLineK is None:
        leftCutLineB = min(upLeftTop[0], upLeftBottom[0], downLeftTop[0], downLeftBottom[0]) - leftRemain
    else:
        upLeftTopX = upLeftTop[1] - (upLeftTop[0] - leftRemain) * leftCutLineK
        upLeftBottomX = upLeftBottom[1] - (upLeftBottom[0] - leftRemain) * leftCutLineK
        downLeftTopX = downLeftTop[1] - (downLeftTop[0] - leftRemain) * leftCutLineK
        downLeftBottomX = downLeftBottom[1] - (downLeftBottom[0] - leftRemain) * leftCutLineK

        if leftCutLineK > 0:  # 当k大于0时，b越大，越靠左
            leftCutLineB = max(upLeftTopX, upLeftBottomX, downLeftTopX, downLeftBottomX)
        else:  # 当k小于0时，b越小，越靠左
            leftCutLineB = min(upLeftTopX, upLeftBottomX, downLeftTopX, downLeftBottomX)

    rightCutLineK = get_linear_fit_k_vertical(np.array([upRightBottom, upRightTop, downRightTop, downRightBottom]))
    # 直接分别求过四个点的直线的b参数，找出最值
    if rightCutLineK is None:
        rightCutLineB = max(upRightTop[0], upRightBottom[0], downRightTop[0], downRightBottom[0]) + rightRemain
    else:
        upRightTopX = upRightTop[1] - (upRightTop[0] + rightRemain) * rightCutLineK
        upRightBottomX = upRightBottom[1] - (upRightBottom[0] + rightRemain) * rightCutLineK
        downRightTopX = downRightTop[1] - (downRightTop[0] + rightRemain) * rightCutLineK
        downRightBottomX = downRightBottom[1] - (downRightBottom[0] + rightRemain) * rightCutLineK

        if rightCutLineK > 0:  # 当k大于0时，b越大，越靠左
            rightCutLineB = min(upRightTopX, upRightBottomX, downRightTopX, downRightBottomX)
        else:  # 当k小于0时，b越小，越靠左
            rightCutLineB = max(upRightTopX, upRightBottomX, downRightTopX, downRightBottomX)

    topCutLineK = get_line_k(upLeftTop, upRightTop)
    if topCutLineK is None:
        cv.destroyAllWindows()
        return False, None, None
    else:
        topCutLineB = topCutLineK * -1 * (upLeftTop[0]) + (upLeftTop[1] - topRemain)

    bottomCutLineK = get_line_k(downLeftBottom, downRightBottom)
    if bottomCutLineK is None:
        # 垂直，一定是异常
        cv.destroyAllWindows()
        return False, None, None, None
    else:
        bottomCutLineB = bottomCutLineK * -1 * (downLeftBottom[0]) + downLeftBottom[1] + bottomRemain

    # 四边都不切的逻辑寻找边线的算法
    boardLeftTop = [0, 0]
    boardRightTop = [w, 0]
    boardLeftBottom = [0, h]
    boardRightBottom = [w, h]

    # 先将切线超过1,3边范围的的留边宽度置为0，也就是如果边线算出去了,就沿着物料区域的最外侧1个mm去切
    leftTopY = upLeftTop[1] - topRemain
    rightTopY = upRightTop[1] - topRemain
    if leftTopY < 0 or rightTopY < 0:
        topRemain = 0

    leftBottomY = downLeftBottom[1] + bottomRemain
    rightBottomY = downRightBottom[1] + bottomRemain
    if leftBottomY > totalBoard.shape[0] or rightBottomY > totalBoard.shape[0]:
        bottomRemain = 0

    if topRemain == 0:
        topLineK, topLineB = get_board_edge_lines(totalBoard, totalBoardThres)
        if topLineK is not None:
            topCutLineK, topCutLineB = topLineK, topLineB
        else:
            topCutLineK, topCutLineB = get_line_k_and_b(boardLeftTop, boardRightTop)
    if bottomRemain == 0:
        bottomCutLineK, bottomCutLineB = get_line_k_and_b(boardLeftBottom, boardRightBottom)
    if leftRemain == 0:
        leftCutLineK, leftCutLineB = get_line_k_and_b(boardLeftBottom, boardLeftTop)
    if rightRemain == 0:
        rightCutLineK, rightCutLineB = get_line_k_and_b(boardRightTop, boardRightBottom)

    # ①求所有的交点，当上下左右四条边都切时

    leftTopCross = get_two_lines_intersection(topCutLineK, topCutLineB, leftCutLineK, leftCutLineB)
    rightTopCross = get_two_lines_intersection(topCutLineK, topCutLineB, rightCutLineK, rightCutLineB)
    leftBottomCross = get_two_lines_intersection(bottomCutLineK, bottomCutLineB, leftCutLineK, leftCutLineB)
    rightBottomCross = get_two_lines_intersection(bottomCutLineK, bottomCutLineB, rightCutLineK, rightCutLineB)

    if leftTopCross[0] is None or rightTopCross[0] is None or leftBottomCross[0] is None or \
            rightBottomCross[0] is None:
        cv.destroyAllWindows()
        return False, None, None, None
    extend = int(8 * cp.mm2pix)

    topCutLeftPoint = [- extend, topCutLineK * (- extend) + topCutLineB]
    topCutRightPoint = [extend + w, topCutLineK * (extend + w) + topCutLineB]

    # bottomCutRightPoint = [w + 10,
    #                        bottomCutLineK * (w + 10) + bottomCutLineB]

    bottomCutRightPoint = [w + extend, bottomCutLineK * (w + extend) + bottomCutLineB]
    bottomCutLeftPoint = [- extend, bottomCutLineK * (- extend) + bottomCutLineB]

    if leftCutLineK is None:
        leftCutLineBottom = [leftCutLineB, leftBottomCross[1] + extend]
        leftCutLineTop = [leftCutLineB, leftTopCross[1] - extend]
    elif leftCutLineK != 0:
        leftCutLineBottom = [((leftBottomCross[1] + extend) - leftCutLineB) / leftCutLineK,
                             leftBottomCross[1] + extend]
        leftCutLineTop = [(leftTopCross[1] - extend - leftCutLineB) / leftCutLineK, leftTopCross[1] - extend]
    else:
        cv.destroyAllWindows()
        return False, None, None, None

    if rightCutLineK is None:
        rightCutLineTop = [rightCutLineB, rightTopCross[1] - extend]
        rightCutLineBottom = [rightCutLineB, rightBottomCross[1] + extend]
    elif rightCutLineK != 0:
        rightCutLineTop = [((rightTopCross[1] - extend) - rightCutLineB) / rightCutLineK,
                           rightTopCross[1] - extend]
        rightCutLineBottom = [((rightBottomCross[1] + extend) - rightCutLineB) / rightCutLineK,
                              rightBottomCross[1] + extend]
    else:
        cv.destroyAllWindows()
        return False, None, None, None

    # 求中线，如果求出来k和top，bottom差太多就返回错误
    middleCutLine = get_middle_cut_line(upLeftBottom, upRightBottom, downLeftTop, downRightTop)

    if middleCutLine[0] is None:
        # 垂直，一定是异常
        cv.destroyAllWindows()
        return False, None, None, None
    leftMiddleCross = get_two_lines_intersection(middleCutLine[0], middleCutLine[1], leftCutLineK, leftCutLineB)
    rightMiddleCross = get_two_lines_intersection(middleCutLine[0], middleCutLine[1], rightCutLineK, rightCutLineB)

    middleCutLineLeft = [leftMiddleCross[0] - 10,
                         (leftMiddleCross[0] - 10) * middleCutLine[0] + middleCutLine[1]]
    middleCutLineRight = [rightMiddleCross[0] + 10,
                          (rightMiddleCross[0] + 10) * middleCutLine[0] + middleCutLine[1]]

    # 变成大图中的点
    # cx, cy表示板的中心点，w，h是板的宽高，rotateAngle表示要旋转的角度
    (cx, cy), (w, h), rotateAngle = totalBoardBox

    wHalf, hHalf = w // 2 - 1, h // 2 - 1
    M = cv.getRotationMatrix2D((wHalf, hHalf), -rotateAngle, 1)

    # 获取物料区域在大图上的四个点的坐标.
    boardLeftTopBig = np.add(np.dot(M, [boardLeftTop[0], boardLeftTop[1], 1]),
                             [cx - wHalf, cy - hHalf])
    boardRightTopBig = np.add(np.dot(M, [boardRightTop[0], boardRightTop[1], 1]),
                              [cx - wHalf, cy - hHalf])
    boardRightBottomBig = np.add(np.dot(M, [boardRightBottom[0], boardRightBottom[1], 1]),
                                 [cx - wHalf, cy - hHalf])
    boardLeftBottomBig = np.add(np.dot(M, [boardLeftBottom[0], boardLeftBottom[1], 1]),
                                [cx - wHalf, cy - hHalf])

    topCutLeftPointBig = np.add(np.dot(M, [topCutLeftPoint[0], topCutLeftPoint[1], 1]),
                                [cx - wHalf, cy - hHalf])
    topCutRightPointBig = np.add(np.dot(M, [topCutRightPoint[0], topCutRightPoint[1], 1]),
                                 [cx - wHalf, cy - hHalf])
    bottomCutRightPointBig = np.add(np.dot(M, [bottomCutRightPoint[0], bottomCutRightPoint[1], 1]),
                                    [cx - wHalf, cy - hHalf])
    bottomCutLeftPointBig = np.add(np.dot(M, [bottomCutLeftPoint[0], bottomCutLeftPoint[1], 1]),
                                   [cx - wHalf, cy - hHalf])
    leftCutLineBottomBig = np.add(np.dot(M, [leftCutLineBottom[0], leftCutLineBottom[1], 1]),
                                  [cx - wHalf, cy - hHalf])
    leftCutLineTopBig = np.add(np.dot(M, [leftCutLineTop[0], leftCutLineTop[1], 1]),
                               [cx - wHalf, cy - hHalf])
    rightCutLineTopBig = np.add(np.dot(M, [rightCutLineTop[0], rightCutLineTop[1], 1]),
                                [cx - wHalf, cy - hHalf])
    rightCutLineBottomBig = np.add(np.dot(M, [rightCutLineBottom[0], rightCutLineBottom[1], 1]),
                                   [cx - wHalf, cy - hHalf])
    middleCutLineLeftBig = np.add(np.dot(M, [middleCutLineLeft[0], middleCutLineLeft[1], 1]),
                                  [cx - wHalf, cy - hHalf])
    middleCutLineRightBig = np.add(np.dot(M, [middleCutLineRight[0], middleCutLineRight[1], 1]),
                                   [cx - wHalf, cy - hHalf])

    # 重新将端点定位到大图上去
    upLeftBottom = np.add(np.dot(M, [upLeftBottom[0], upLeftBottom[1], 1]), [cx - wHalf, cy - hHalf])
    upRightBottom = np.add(np.dot(M, [upRightBottom[0], upRightBottom[1], 1]), [cx - wHalf, cy - hHalf])
    upLeftTop = np.add(np.dot(M, [upLeftTop[0], upLeftTop[1], 1]), [cx - wHalf, cy - hHalf])
    upRightTop = np.add(np.dot(M, [upRightTop[0], upRightTop[1], 1]), [cx - wHalf, cy - hHalf])
    downLeftTop = np.add(np.dot(M, [downLeftTop[0], downLeftTop[1], 1]), [cx - wHalf, cy - hHalf])
    downLeftBottom = np.add(np.dot(M, [downLeftBottom[0], downLeftBottom[1], 1]), [cx - wHalf, cy - hHalf])
    downRightTop = np.add(np.dot(M, [downRightTop[0], downRightTop[1], 1]), [cx - wHalf, cy - hHalf])
    downRightBottom = np.add(np.dot(M, [downRightBottom[0], downRightBottom[1], 1]), [cx - wHalf, cy - hHalf])

    # 画出内框
    cv.line(imageColor, (int(upLeftBottom[0]), int(upLeftBottom[1])), (int(upRightBottom[0]), int(upRightBottom[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(upLeftBottom[0]), int(upLeftBottom[1])), (int(upLeftTop[0]), int(upLeftTop[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(upRightBottom[0]), int(upRightBottom[1])), (int(upRightTop[0]), int(upRightTop[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(upLeftTop[0]), int(upLeftTop[1])), (int(upRightTop[0]), int(upRightTop[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(downLeftBottom[0]), int(downLeftBottom[1])), (int(downRightBottom[0]), int(downRightBottom[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(downLeftBottom[0]), int(downLeftBottom[1])), (int(downLeftTop[0]), int(downLeftTop[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(downRightBottom[0]), int(downRightBottom[1])), (int(downRightTop[0]), int(downRightTop[1])), (0, 0, 255), 3)
    cv.line(imageColor, (int(downLeftTop[0]), int(downLeftTop[1])), (int(downRightTop[0]), int(downRightTop[1])), (0, 0, 255), 3)

    # 画出上，下，左，右，中五条切线

    cv.line(imageColor, (int(topCutLeftPointBig[0]), int(topCutLeftPointBig[1])),
            (int(topCutRightPointBig[0]), int(topCutRightPointBig[1])), (0, 255, 0), 3)
    cv.putText(imageColor, '1', (int((topCutLeftPointBig[0] + topCutRightPointBig[0]) / 2),
                                 int((topCutLeftPointBig[1] + topCutRightPointBig[1]) / 2) - 60), cv.FONT_HERSHEY_COMPLEX, 4, (255, 0, 0),
               12)
    cv.line(imageColor, (int(bottomCutRightPointBig[0]), int(bottomCutRightPointBig[1])),
            (int(bottomCutLeftPointBig[0]), int(bottomCutLeftPointBig[1])), (0, 255, 0), 3)
    cv.putText(imageColor, '3', (int((bottomCutRightPointBig[0] + bottomCutLeftPointBig[0]) / 2),
                                 int((bottomCutRightPointBig[1] + bottomCutLeftPointBig[1]) / 2) + 140), cv.FONT_HERSHEY_COMPLEX,
               4, (255, 0, 0), 12)

    cv.line(imageColor, (int(leftCutLineBottomBig[0]), int(leftCutLineBottomBig[1])),
            (int(leftCutLineTopBig[0]), int(leftCutLineTopBig[1])), (255, 0, 0), 3)
    cv.putText(imageColor, '2', (int((leftCutLineBottomBig[0] + leftCutLineTopBig[0]) / 2) - 110,
                                 int((leftCutLineBottomBig[1] + leftCutLineTopBig[1]) / 2)), cv.FONT_HERSHEY_COMPLEX,
               4, (255, 0, 0), 12)
    cv.line(imageColor, (int(rightCutLineTopBig[0]), int(rightCutLineTopBig[1])),
            (int(rightCutLineBottomBig[0]), int(rightCutLineBottomBig[1])), (255, 0, 0), 3)
    cv.putText(imageColor, '4', (int((rightCutLineTopBig[0] + rightCutLineBottomBig[0]) / 2) + 30,
                                 int((rightCutLineTopBig[1] + rightCutLineBottomBig[1]) / 2)), cv.FONT_HERSHEY_COMPLEX,
               4, (255, 0, 0), 12)
    cv.line(imageColor, (int(middleCutLineLeftBig[0]), int(middleCutLineLeftBig[1])),
            (int(middleCutLineRightBig[0]), int(middleCutLineRightBig[1])), (0, 255, 0), 3)

    # 综合10个点的信息
    topCutLeftPointRobot = pix_pos_to_robot(topCutLeftPointBig, transMtx)
    topCutRightPointRobot = pix_pos_to_robot(topCutRightPointBig, transMtx)
    bottomCutLeftPointRobot = pix_pos_to_robot(bottomCutLeftPointBig, transMtx)
    bottomCutRightPointRobot = pix_pos_to_robot(bottomCutRightPointBig, transMtx)
    leftCutLineBottomRobot = pix_pos_to_robot(leftCutLineBottomBig, transMtx)
    leftCutLineTopRobot = pix_pos_to_robot(leftCutLineTopBig, transMtx)
    rightCutLineTopRobot = pix_pos_to_robot(rightCutLineTopBig, transMtx)
    rightCutLineBottomRobot = pix_pos_to_robot(rightCutLineBottomBig, transMtx)
    middleCutLineLeftRobot = pix_pos_to_robot(middleCutLineLeftBig, transMtx)
    middleCutLineRightRobot = pix_pos_to_robot(middleCutLineRightBig, transMtx)

    boardLeftTopRobot = pix_pos_to_robot(boardLeftTopBig, transMtx)
    boardRightTopRobot = pix_pos_to_robot(boardRightTopBig, transMtx)
    boardRightBottomRobot = pix_pos_to_robot(boardRightBottomBig, transMtx)
    boardLeftBottomRobot = pix_pos_to_robot(boardLeftBottomBig, transMtx)
    # 计算裁切面积和刀使用的长度
    cutDisTotal = 0
    cutAreaTotal = 0
    if topRemain != 0:
        cutDis = distance(topCutLeftPointRobot, topCutRightPointRobot)
        area = cutDis * (distance(get_two_points_middle(topCutLeftPointRobot, topCutRightPointRobot),
                                  get_two_points_middle(boardLeftTopRobot, boardRightTopRobot)))
        cutAreaTotal += area
        cutDisTotal += cutDis
    if leftRemain != 0:
        cutDis = distance(leftCutLineBottomRobot, leftCutLineTopRobot)
        area = cutDis * (distance(get_two_points_middle(leftCutLineBottomRobot, leftCutLineTopRobot),
                                  get_two_points_middle(boardLeftTopRobot, boardLeftBottomRobot)))
        cutAreaTotal += area
        cutDisTotal += cutDis

    if bottomRemain != 0:
        cutDis = distance(bottomCutRightPointRobot, bottomCutLeftPointRobot)
        area = cutDis * (distance(get_two_points_middle(bottomCutRightPointRobot, bottomCutLeftPointRobot),
                                  get_two_points_middle(boardLeftBottomRobot, boardRightBottomRobot)))
        cutAreaTotal += area
        cutDisTotal += cutDis

    if rightRemain != 0:
        cutDis = distance(rightCutLineTopRobot, rightCutLineBottomRobot)
        area = cutDis * (distance(get_two_points_middle(rightCutLineTopRobot, rightCutLineBottomRobot),
                                  get_two_points_middle(boardRightTopRobot, boardRightBottomRobot)))
        cutAreaTotal += area
        cutDisTotal += cutDis

    # cv.imwrite("d:/cutLineImage/%s.jpg" % (time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime())), cutLineImage)
    imageResut = get_board_widthout_angle(imageColor, totalBoardBox, extend=True)
    # imageResut = cv.resize(imageResut, (imageResut.shape[1] // 2, imageResut.shape[0] // 2), cv.INTER_AREA)
    image_show("resultImage", imageResut)
    # 以裁切机的实际切线为标准，计算前板长，后板长，总长
    # 先算中线中点
    firstBoardLength, totalBoardLength = get_board_length([topCutLeftPointRobot, topCutRightPointRobot],
                                                          [middleCutLineLeftRobot, middleCutLineRightRobot],
                                                          [bottomCutLeftPointRobot, bottomCutRightPointRobot])
    # 计算top_middle 和bottom_middle的角度,全部调整成一个方向,需要逆时针调整的时候为负数.
    topMiddleAngle = get_two_line_angle(middleCutLineRightRobot, middleCutLineLeftRobot, topCutRightPointRobot, topCutLeftPointRobot)  # 获取第一个角度
    bottomMiddleAngle = get_two_line_angle(middleCutLineRightRobot, middleCutLineLeftRobot, bottomCutRightPointRobot,
                                           bottomCutLeftPointRobot)  # 获取第二个角度
    # 是否切第一刀:
    isFirstCut = True if topRemain != 0 else False
    isThirdCut = True if bottomRemain != 0 else False
    cutLength = [round(cutDisTotal, 5), round(cutAreaTotal, 5)]
    cutWidth = distance(topCutLeftPointRobot, topCutRightPointRobot)
    return [topCutLeftPointRobot, topCutRightPointRobot, bottomCutRightPointRobot, bottomCutLeftPointRobot,
            leftCutLineBottomRobot, leftCutLineTopRobot, rightCutLineTopRobot, rightCutLineBottomRobot, middleCutLineLeftRobot,
            middleCutLineRightRobot, firstBoardLength, totalBoardLength, topMiddleAngle,
            bottomMiddleAngle], imageResut, cutLength, isFirstCut, isThirdCut, cutWidth


def check_total_board_is_ok(imageSrc, width, height, boardBreak, totalHeightMax, totalHeightMin):
    """
    :param imageSrc:
    :param width:
    :param height:
    :param boardBreak:
    :param totalHeightMax 物料检测的最大值
    :param totalHeightMin 物料检测的最小值
    :return:
    """
    tablePos = int(np.mean(imageSrc[cp.sensorPos - 100:cp.sensorPos, cp.tablePosStart:cp.tablePosEnd]))
    sensorPos = int(np.mean(imageSrc[cp.sensorPos:cp.sensorPos + 100, cp.tablePosStart:cp.tablePosEnd]))
    totalBoardBox, totalBoardThres, totalBoardRes = find_total_board(imageSrc, width, height)
    w, h = totalBoardBox[1]
    widthChecked, heightChecked = w / cp.mm2pix, h / cp.mm2pix
    if not boardBreak:
        maxThres = totalHeightMax
        minThres = totalHeightMin
    else:
        maxThres = totalHeightMax + 200
        minThres = totalHeightMin
    retInfo = "传感器Pix = {} 最小值 = {},台布Pix = {} 最大值 = {},高度 = {:.2f},阈值 = ({},{})".format(sensorPos, cp.sensorValUsed, tablePos,
                                                                                         cp.tableValUsed, heightChecked, minThres, maxThres)
    totalBoardRes = put_table_sensor_pos_to_image(totalBoardRes, tablePos, sensorPos)
    if sensorPos < cp.sensorValUsed or tablePos > cp.tableValUsed or heightChecked < minThres or heightChecked > maxThres:
        # 将检测的区域也画上去.
        return False, retInfo, totalBoardBox, totalBoardThres, totalBoardRes
    else:
        return True, retInfo, totalBoardBox, totalBoardThres, totalBoardRes


def get_width_height_by_image_name(imageName):
    """
    根据图像
    :param imageName: 要处理的图片文件名称,合格的名称包括w_和h_这样的字符串,例如:10--11111--04-12_16-01--w_1552--h_1386--u_1_2_3_4--d_5_6_7_8.bmp
    :return:
    """
    if "w_" not in imageName or "h_" not in imageName:
        return None, None, None, None
    widthIndex = imageName.index("w_")
    heightIndex = imageName.index("h_")
    nameW = imageName[widthIndex + 2:widthIndex + 6]
    nameH = imageName[heightIndex + 2:heightIndex + 6]

    if "u_" not in imageName or "d_" not in imageName:
        upOffsetList = [0, 0, 0, 0]
        downOffsetList = [0, 0, 0, 0]
    else:
        uIndexStart = imageName.index("u_")
        dIndexStart = imageName.index("d_")
        uIndexEnd = imageName.index("--d")
        dIndexEnd = imageName.index(".bmp")

        upOffsetList = imageName[uIndexStart + 2:uIndexEnd].split("_")
        downOffsetList = imageName[dIndexStart + 2:dIndexEnd].split("_")
        upOffsetList = [int(offset) for offset in upOffsetList]
        downOffsetList = [int(offset) for offset in downOffsetList]

    return int(nameW), int(nameH), upOffsetList, downOffsetList


if __name__ == '__main__':
    # 先加载参数
    filePath = r"D:\raw_fail_test"
    isFirstBoard = False  # 是否是首片,首片有首片的算法逻辑
    res, data = db.get_line_cam_check_thres()
    totalHeightMax = data.get("totalHeightMax")
    totalHeightMin = data.get("totalHeightMin")
    boardBreak = False
    calcThresBig = 20
    calcThresSmall = 9
    innerToEdgeThres = 5
    mtx = [
        [0, 1, 0],
        [1, 0, 0]
    ]
    remainEdge = [1, 1, 1, 1]
    topRemain, leftRemain, bottomRemain, rightRemain = [int(x * cp.mm2pix) for x in remainEdge]
    keepDir = filePath
    if not os.path.isdir(keepDir):
        keepDir, _ = os.path.split(filePath)

    keepOkPath = os.path.join(keepDir, "ok")
    keepOkResPath = os.path.join(keepDir, "cutlines")
    keepFailPath = os.path.join(keepDir, "fail")
    if not os.path.exists(keepOkPath):
        os.mkdir(keepOkPath)
    if not os.path.exists(keepOkResPath):
        os.mkdir(keepOkResPath)
    if not os.path.exists(keepFailPath):
        os.mkdir(keepFailPath)

    fileNames = []  # 保存要去识别的所有的图片
    if os.path.isfile(filePath):
        filePath, fileName = os.path.split(filePath)
        fileNames.append(fileName)
    else:
        for root, dirs, files in os.walk(filePath):
            if root != filePath:
                continue
            for file in files:
                if file[-4:] != ".bmp":
                    continue
                fileNames.append(file)

    for index, fileName in enumerate(fileNames):
        filePathReal = os.path.join(filePath, fileName)
        srcW, srcH, upLineOffset, downLineOffset = get_width_height_by_image_name(fileName)
        if srcW is None or srcH is None:
            print("*" * 10 + "第 {} 张图 ({}),图像名称错误,已经跳过".format(index + 1, fileName) + "*" * 10)
            continue
        print("*" * 10 + " 第 {} 张图 ({})".format(index + 1, fileName) + "*" * 10)
        imageTest = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)
        calcStart = time.time()
        checkRes, checkInfo, totalBoardBox, boardThres, totalBoardRes = check_total_board_is_ok(imageTest, srcW, srcH, boardBreak,
                                                                                                totalHeightMax, totalHeightMin)
        if checkRes:
            print("物料检测通过: {}".format(checkInfo))
        else:
            print("物料检测未通过: {}".format(checkInfo))

        if isFirstBoard:
            res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes_first(imageTest, srcW, srcH, calcThresBig, calcThresSmall,
                                                                                     totalBoardBox, boardThres)
            upLineOffset, downLineOffset = get_line_offset(upBox, downBox, totalBoard, totalBoardBox)
            print("upLineOffset = {},downLineOffset = {}".format(upLineOffset, downLineOffset))
        else:
            res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes(imageTest, srcW, srcH, calcThresBig,
                                                                               calcThresSmall, innerToEdgeThres,
                                                                               totalBoardBox, boardThres,
                                                                               upLineOffset, downLineOffset)

        if res:
            cutLines, imageRes, cutDis, isFirstCut, isThirdCut, cutWidth = get_lines_with_boxes(imageTest, totalBoard, totalBoardBox, totalBoardThres,
                                                                                                upBox, downBox, mtx, topRemain, leftRemain,
                                                                                                bottomRemain,
                                                                                                rightRemain)
            print("算法耗时: {}".format(time.time() - calcStart))
            os.remove(filePathReal)
            cv.imwrite(os.path.join(keepOkResPath, fileName), imageRes)
            cv.imwrite(os.path.join(keepOkPath, fileName), imageTest)
            print("识别成功")
            continue
        else:
            print("算法耗时: {}".format(time.time() - calcStart))
            cv.imwrite(os.path.join(keepFailPath, fileName), imageTest)
            os.remove(filePathReal)
            print("识别失败XXXXXXXXXXXXXXXXX")
            continue
